// 单核物理页分配器（基于空闲页单链表）
#include "pmm.h"

// 空闲链表节点：直接复用空闲页本身的前几个字节存放 next 指针
struct run {
    struct run *next;
};

static struct {
    struct run *head; // 空闲链表表头
    size_t free_pages;
} freelist = { 0 };

extern char end[]; // 链接脚本提供：内核镜像结束地址

static inline int is_page_aligned(uint64 addr) {
    return (addr & (PGSIZE - 1)) == 0;
}

static void push_page(void *pa) {
    struct run *r = (struct run *)pa;
    r->next = freelist.head;
    freelist.head = r;
    freelist.free_pages++;
}

static void *pop_page(void) {
    struct run *r = freelist.head;
    if (!r) return 0;
    freelist.head = r->next;
    freelist.free_pages--;
    return (void *)r;
}

void pmm_init(void) {
      // 从内核镜像末尾按页对齐开始，直到 PHYSTOP 建立空闲页链表
    uint64 pa_start = PGROUNDUP((uint64)end);
    uint64 pa_end = PHYSTOP;

    /// 逐页入链
    for (uint64 pa = pa_start; pa + PGSIZE <= pa_end; pa += PGSIZE) {
        push_page((void *)pa);
    }
}

void *alloc_page(void) {
    return pop_page();
}

void free_page(void *pa) {
    if (pa == 0) return;
    uint64 addr = (uint64)pa;
      // 基本校验：页对齐、地址范围
    if (!is_page_aligned(addr)) return;
    if (addr < PGROUNDUP((uint64)end) || addr + PGSIZE > PHYSTOP) return;
    push_page(pa);
}

void *alloc_pages(int n) {
    if (n <= 0) return 0;
    if (n == 1) return alloc_page();

     // 简单实现：循环申请 n 次单页，不保证物理连续；若失败则回滚
    void *first = 0;
    void *saved[64];
    if (n > (int)(sizeof(saved)/sizeof(saved[0]))) return 0;
    int i = 0;
    for (; i < n; ++i) {
        void *p = alloc_page();
        if (!p) break;
        saved[i] = p;
        if (i == 0) first = p;
    }
    if (i != n) {
        while (i-- > 0) free_page(saved[i]);
        return 0;
    }
    return first;
}

size_t pmm_free_pages(void) {
    return freelist.free_pages;
}


